using System;
using Headspring.Training.Core.Model;
using Headspring.Training.Core.Services;
using Headspring.Training.Infrastructure.Impl;
using NHibernate;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using StructureMap;

namespace Headspring.Training.IntegrationTests
{
	[TestFixture]
	public class WorkOrderRepositoryTester : DataTestBase
	{
		[Test]
		public void ShouldGetLatestChangeDate()
		{
			var employee1 = new Employee("1", "2", "1", "1");
			var employee2 = new Employee("2", "3", "2", "2");
			var order1 = new WorkOrder();
			order1.Creator = employee2;
			order1.Assignee = employee1;
			order1.CreatedDate = new DateTime(1999, 1, 1);
			order1.Number = "123";
			order1.Status = WorkOrderStatus.Draft;

			order1.AddHistory(new HistoryEntry(employee1, WorkOrderStatus.Complete,
			                                   WorkOrderStatus.Complete,
			                                   new DateTime(1999, 1, 1)));
			order1.AddHistory(new HistoryEntry(employee2, WorkOrderStatus.Draft,
			                                   WorkOrderStatus.Draft,
			                                   new DateTime(2001, 1, 1)));
			order1.AddHistory(new HistoryEntry(employee2, WorkOrderStatus.Assigned,
			                                   WorkOrderStatus.Draft,
			                                   new DateTime(2000, 1, 1)));

			ISession session = getSession();
			session.SaveOrUpdate(employee1);
			session.SaveOrUpdate(employee2);
			session.Flush();

			var repository = ObjectFactory.FillDependencies<WorkOrderRepository>();
			repository.Save(order1);

			session.Dispose(); //kill any NHibernate cache that might exist

			WorkOrder rehydratedWorkOrder = repository.GetWorkOrder(order1.Number);

			Assert.That(rehydratedWorkOrder.LastChangeDate,
			            Is.EqualTo(new DateTime(2001, 1, 1)));
		}

		[Test]
		public void ShouldGetWorkOrderByNumber()
		{
			TestHelper.ClearTables();

			var creator = new Employee("1", "1", "1", "1");
			var order1 = new WorkOrder
			             	{
			             		Creator = creator,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123"
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = creator,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456"
			             	};


			ISession session = getSession();
			session.SaveOrUpdate(creator);
			session.SaveOrUpdate(order1);
			session.SaveOrUpdate(order2);
			session.Flush();
			session.Dispose();

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			WorkOrder order123 = repository.GetWorkOrder("123");

			Assert.That(order123.Id, Is.EqualTo(order1.Id));

			WorkOrder order456 = repository.GetWorkOrder("456");

			Assert.That(order456.Id, Is.EqualTo(order2.Id));
		}

		[Test]
		public void ShouldPersistHistory()
		{
			var employee1 = new Employee("1", "2", "1", "1");
			var employee2 = new Employee("2", "3", "2", "2");
			var order1 = new WorkOrder();
			order1.Creator = employee2;
			order1.Assignee = employee1;
			order1.CreatedDate = new DateTime(1999, 1, 1);
			order1.Number = "123";
			order1.Status = WorkOrderStatus.Draft;

			order1.AddHistory(new HistoryEntry(employee1, WorkOrderStatus.Complete,
			                                   WorkOrderStatus.Complete,
			                                   new DateTime(1999, 1, 1)));
			order1.AddHistory(new HistoryEntry(employee2, WorkOrderStatus.Draft,
			                                   WorkOrderStatus.Draft,
			                                   new DateTime(2000, 1, 1)));

			ISession session = getSession();
			session.SaveOrUpdate(employee1);
			session.SaveOrUpdate(employee2);
			session.Flush();

			var repository = ObjectFactory.FillDependencies<WorkOrderRepository>();
			repository.Save(order1);

			session.Dispose(); //kill any NHibernate cache that might exist

			WorkOrder rehydratedWorkOrder = repository.GetWorkOrder(order1.Number);
			HistoryEntry[] entries = rehydratedWorkOrder.GetHistory();

			Assert.That(entries.Length, Is.EqualTo(2));
			Assert.That(entries[0].Employee.Id, Is.EqualTo(employee2.Id));
			Assert.That(entries[0].EmployeeFirstName, Is.EqualTo("3"));
			Assert.That(entries[0].EmployeeLastName, Is.EqualTo("2"));
			Assert.That(entries[0].ChangeDate, Is.EqualTo(new DateTime(2000, 1, 1)));
			Assert.That(entries[0].BeginStatus,
			            Is.EqualTo(WorkOrderStatus.Draft));
			Assert.That(entries[0].EndStatus,
			            Is.EqualTo(WorkOrderStatus.Draft));

			Assert.That(entries[1].Employee.Id, Is.EqualTo(employee1.Id));
			Assert.That(entries[1].EmployeeFirstName, Is.EqualTo("2"));
			Assert.That(entries[1].EmployeeLastName, Is.EqualTo("1"));
			Assert.That(entries[1].ChangeDate, Is.EqualTo(new DateTime(1999, 1, 1)));
			Assert.That(entries[1].BeginStatus,
			            Is.EqualTo(WorkOrderStatus.Complete));
			Assert.That(entries[1].EndStatus, Is.EqualTo(WorkOrderStatus.Complete));
		}

		[Test]
		public void ShouldReturnSearchResultsByLastChangedDateDescendingOrder()
		{
			var employee1 = new Employee("1", "1", "1", "1");
			var employee2 = new Employee("2", "2", "2", "2");
			var order1 = new WorkOrder
			             	{
			             		Creator = employee2,
			             		Assignee = employee1,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123",
			             		Status = WorkOrderStatus.Assigned
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = employee1,
			             		Assignee = employee2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456",
			             		Status = WorkOrderStatus.Draft
			             	};
			var order3 = new WorkOrder
			             	{
			             		Creator = employee1,
			             		Assignee = employee2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "789",
			             		Status = WorkOrderStatus.Draft
			             	};

			order1.ChangeStatus(WorkOrderStatus.Assigned, employee1,
			                    new DateTime(2000, 1, 1));
			order1.ChangeStatus(WorkOrderStatus.Complete, employee1,
			                    new DateTime(2001, 1, 1));

			order2.ChangeStatus(WorkOrderStatus.Assigned, employee1,
			                    new DateTime(1999, 1, 1));
			order2.ChangeStatus(WorkOrderStatus.Complete, employee1,
			                    new DateTime(2005, 1, 1));

			order3.ChangeStatus(WorkOrderStatus.Assigned, employee1,
			                    new DateTime(2001, 1, 1));
			order3.ChangeStatus(WorkOrderStatus.Complete, employee1,
			                    new DateTime(2004, 1, 1));

			using (ISession session = getSession())
			{
				session.SaveOrUpdate(employee1);
				session.SaveOrUpdate(employee2);
				session.SaveOrUpdate(order1);
				session.SaveOrUpdate(order2);
				session.SaveOrUpdate(order3);
				session.Flush();
			}

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			var specification = new WorkOrderSearchSpecification();

			WorkOrder[] orders = repository.FindWorkOrders(specification);

			Assert.That(orders.Length, Is.EqualTo(3));
			Assert.That(orders[0].Id, Is.EqualTo(order2.Id));
			Assert.That(orders[1].Id, Is.EqualTo(order3.Id));
			Assert.That(orders[0].LastChangeDate, Is.EqualTo(new DateTime(2005, 1, 1)));
		}

		[Test]
		public void ShouldSaveWorkOrder()
		{
			TestHelper.ClearTables();

			var creator = new Employee("1", "1", "1", "1");
			var assignee = new Employee("2", "2", "2", "2");
			var order = new WorkOrder
			            	{
			            		Creator = creator,
			            		Assignee = assignee,
			            		CreatedDate = new DateTime(1999, 1, 1),
			            		CompletedDate = new DateTime(1999, 1, 2),
			            		Title = "foo",
			            		Description = "bar",
			            		Status = WorkOrderStatus.InProgress,
			            		Number = "123"
			            	};

			ISession session = getSession();
			session.SaveOrUpdate(creator);
			session.SaveOrUpdate(assignee);
			session.Flush();

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			repository.Save(order);

			session.Dispose();

			ISession session2 = getSession();
			var rehydratedWorkOrder = session2.Load<WorkOrder>(order.Id);
			Assert.That(rehydratedWorkOrder.Id, Is.EqualTo(order.Id));
			Assert.That(rehydratedWorkOrder.Creator.Id,
			            Is.EqualTo(order.Creator.Id));
			Assert.That(rehydratedWorkOrder.Assignee.Id,
			            Is.EqualTo(order.Assignee.Id));
			Assert.That(rehydratedWorkOrder.CreatedDate,
			            Is.EqualTo(order.CreatedDate));
			Assert.That(rehydratedWorkOrder.CompletedDate,
			            Is.EqualTo(order.CompletedDate));
			Assert.That(rehydratedWorkOrder.Title, Is.EqualTo(order.Title));
			Assert.That(rehydratedWorkOrder.Description,
			            Is.EqualTo(order.Description));
			Assert.That(rehydratedWorkOrder.Status, Is.EqualTo(order.Status));
			Assert.That(rehydratedWorkOrder.Number, Is.EqualTo(order.Number));
		}

		[Test]
		public void ShouldSearchBySpecificationWithAssignee()
		{
			TestHelper.ClearTables();

			var employee1 = new Employee("1", "1", "1", "1");
			var employee2 = new Employee("2", "2", "2", "2");
			var order1 = new WorkOrder
			             	{
			             		Creator = employee2,
			             		Assignee = employee1,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123"
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = employee1,
			             		Assignee = employee2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456"
			             	};

			ISession session = getSession();
			session.SaveOrUpdate(employee1);
			session.SaveOrUpdate(employee2);
			session.SaveOrUpdate(order1);
			session.SaveOrUpdate(order2);
			session.Flush();
			session.Dispose();

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			var specification = new WorkOrderSearchSpecification();
			specification.MatchAssignee(employee1);
			WorkOrder[] orders = repository.FindWorkOrders(specification);

			Assert.That(orders.Length, Is.EqualTo(1));
			Assert.That(orders[0].Id, Is.EqualTo(order1.Id));
		}

		[Test]
		public void ShouldSearchBySpecificationWithCreator()
		{
			TestHelper.ClearTables();

			var creator1 = new Employee("1", "1", "1", "1");
			var creator2 = new Employee("2", "2", "2", "2");
			var order1 = new WorkOrder
			             	{
			             		Creator = creator1,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123"
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = creator2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456"
			             	};

			ISession session = getSession();
			session.SaveOrUpdate(creator1);
			session.SaveOrUpdate(creator2);
			session.SaveOrUpdate(order1);
			session.SaveOrUpdate(order2);
			session.Flush();
			session.Dispose();

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			var specification = new WorkOrderSearchSpecification();
			specification.MatchCreator(creator1);
			WorkOrder[] orders = repository.FindWorkOrders(specification);

			Assert.That(orders.Length, Is.EqualTo(1));
			Assert.That(orders[0].Id, Is.EqualTo(order1.Id));
		}

		[Test]
		public void ShouldSearchBySpecificationWithFullSpecification()
		{
			var employee1 = new Employee("1", "1", "1", "1");
			var employee2 = new Employee("2", "2", "2", "2");
			var order1 = new WorkOrder
			             	{
			             		Creator = employee2,
			             		Assignee = employee1,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123",
			             		Status = WorkOrderStatus.Assigned
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = employee1,
			             		Assignee = employee2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456",
			             		Status = WorkOrderStatus.Draft
			             	};

			using (ISession session = getSession())
			{
				session.SaveOrUpdate(employee1);
				session.SaveOrUpdate(employee2);
				session.SaveOrUpdate(order1);
				session.SaveOrUpdate(order2);
				session.Flush();
			}

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			var specification = new WorkOrderSearchSpecification();
			specification.MatchStatus(WorkOrderStatus.Assigned);
			specification.MatchCreator(employee2);
			specification.MatchAssignee(employee1);

			WorkOrder[] orders = repository.FindWorkOrders(specification);

			Assert.That(orders.Length, Is.EqualTo(1));
			Assert.That(orders[0].Id, Is.EqualTo(order1.Id));
		}

		[Test]
		public void ShouldSearchBySpecificationWithStatus()
		{
			TestHelper.ClearTables();

			var employee1 = new Employee("1", "1", "1", "1");
			var employee2 = new Employee("2", "2", "2", "2");
			var order1 = new WorkOrder
			             	{
			             		Creator = employee2,
			             		Assignee = employee1,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "123",
			             		Status = WorkOrderStatus.Assigned
			             	};
			var order2 = new WorkOrder
			             	{
			             		Creator = employee1,
			             		Assignee = employee2,
			             		CreatedDate = new DateTime(1999, 1, 1),
			             		Number = "456",
			             		Status = WorkOrderStatus.Draft
			             	};

			ISession session = getSession();
			session.SaveOrUpdate(employee1);
			session.SaveOrUpdate(employee2);
			session.SaveOrUpdate(order1);
			session.SaveOrUpdate(order2);
			session.Flush();
			session.Dispose();

			IWorkOrderRepository repository =
				ObjectFactory.FillDependencies<WorkOrderRepository>();
			var specification = new WorkOrderSearchSpecification();
			specification.MatchStatus(WorkOrderStatus.Assigned);
			WorkOrder[] orders = repository.FindWorkOrders(specification);

			Assert.That(orders.Length, Is.EqualTo(1));
			Assert.That(orders[0].Id, Is.EqualTo(order1.Id));
		}
	}
}